import argparse
import sqlite3
import os
from datetime import datetime

DEBUG = False;
VERSION = "0.1";
AUTHOR = "Lei Xiao";
PROGRAM = "echo";
PREFIX = ":) ";
DB_FILE = "echo.db";
DB_SQL_FILE = "echo.sql";
CURRENT_USER_ID = 1;
COMMANDS = ["modify", "remove"];

parser = argparse.ArgumentParser(prog=PROGRAM, description="Let's chat!", epilog="For example: hello");
parser.add_argument("--user", "-u", required=True, default="User", help="Your name");
parser.add_argument("--version", "-v", action="version", version=PROGRAM + " " + VERSION);
parser.add_argument("--gender", "-g", default="f", choices=["f", "m", "female", "male", "F", "M"], help="Your gender.");

args = parser.parse_args();

if DEBUG:
    print(args);

print(PREFIX + "Hello{0}".format(", " + args.user + "! What can I help with you?" if args.user else "!"));

def db_execute(sql, args=()):
    if sql:
        log(sql);
        conn = sqlite3.connect(DB_FILE);
        conn.row_factory = sqlite3.Row;
        conn.text_factory = str;
        with conn:
            cur = conn.cursor();
            cur.execute(sql, args);
            conn.commit();

            if sql.lstrip().upper().startswith("SELECT"):
                return cur.fetchall();
            elif sql.lstrip().upper().startswith("INSERT"):
                return cur.lastrowid;
            else:
                return cur.rowcount;

    return None;

def initialize_database():
    """
    Create the backend database if not exists.
    """
    if os.path.exists(DB_FILE):
        return True;

    f = open(DB_SQL_FILE, "r");
    while True:
        line = f.readline();
        if line:
            db_execute(line);
        else:
            break;
     
    if DEBUG:
        print("Successfully initialized database.");
    return True;

def search_in_memory(user_id, question):
    """
    Search the answer to the question in memory for the specified user.
    """
    if user_id and question:
        rows = db_execute("SELECT * FROM conversation where UserId = ? and Question like ?", (user_id, question));
        if rows and len(rows) > 0:
            return rows[0]["Answer"];
    else:
        return None;

def search(user_id, question):
    """
    Search the answer to the question in many ways.
    """
    return search_in_memory(user_id, question);

def new_question_answer(user_id, question, answer):
    if question and answer:
        new_id = db_execute("insert into conversation(UserId, Question, Answer, CreationTime, LastUsedTime) values(?, ?, ?, datetime('now', 'localtime'), datetime('now', 'localtime'))", (user_id, question, answer));
        return new_id;
    return None;

def update_answer(user_id, question, answer):
    if question:
        result = db_execute("update conversation set Answer = ? Where UserId = ? and Question like ?", (answer, user_id, question));
        return result;
    return 0;

def set_current_user(display_name):
    if not display_name:
        raise Exception("User display name cannot be empty.");

    global CURRENT_USER_ID;
    user = db_execute("select Id from User where DisplayName like ?", (display_name,));
    if not user or len(user) <= 0:
        new_user_id = db_execute("Insert into user(DisplayName, Gender, CreationTime) values(?, ?, datetime('now', 'localtime'))", (display_name, args.gender));
        CURRENT_USER_ID = new_user_id;
    else:
        CURRENT_USER_ID = user[0][0];

def show_message(message):
    if message:
        print(PREFIX + message);

def input_message(message):
    if message:
        try:
            return raw_input(PREFIX + message);
        except KeyboardInterrupt as ex:
            print("You canceled.");
            pass;

def log(message):
    if DEBUG and message:
        print("[LOG] " + message);
    
def cmd_now(question):
    show_message(str(datetime.now()));

# Define build commands: Type some question will call some specified function.
BUILDIN_COMMANDS = {"now": cmd_now};

# Initialize backend database.
initialize_database();
set_current_user(args.user);

if CURRENT_USER_ID <= 0:
    raise Exception("Failed to recognize the current user.");

log("Current user id: {0}".format(CURRENT_USER_ID));
answer = "";
last_question = "";

while True:
    question = raw_input(PREFIX);
    question = question.strip();
    question_lower_case = question.lower();

    # Remove the last punctuation.
    if question:
        if question.endswith(("?", ".", ",", ";", "!")):
            question = question[:len(question) - 1];

    log("You typed: {0}".format(question));
    if question and question_lower_case in ["quit", "bye", "exit"]:
        print("Bye!\n"); 
        break;
    elif BUILDIN_COMMANDS.has_key(question_lower_case):
        answer = "";
        BUILDIN_COMMANDS[question_lower_case](question);
    elif last_question and answer and (question_lower_case in COMMANDS):
        index = COMMANDS.index(question_lower_case); 
        if index == 0:
            new_answer = input_message("Please type the new answer to the question: \"{0}\": ".format(last_question));
            if new_answer:
                new_answer = new_answer.strip();
                effect_rows = update_answer(CURRENT_USER_ID, last_question, new_answer);
                if effect_rows > 0:
                    show_message("Answer successfully updated.");
                else:
                    show_message("Failed to update the new answer.");
    else:
        log("Search question for user id {0}: {1}".format(str(CURRENT_USER_ID), question));
        answer = search(CURRENT_USER_ID, question);

        if answer:
            last_question = question;
            show_message(answer);
        else:
            user_preferred_answer = input_message("Sorry, I don't understand. What's your preferred answer?");
            if user_preferred_answer:
                new_id = new_question_answer(CURRENT_USER_ID, question,  user_preferred_answer);
                if new_id and new_id > 0:
                    show_message("I got it. Next time, I will give the answer your prefered.");
                else:
                   show_message("Sorry, I cann't recognize.");
